package com.hnust.root.web.aspect;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpUtil;
import cn.hutool.log.Log;
import cn.hutool.log.LogFactory;
import com.hnust.root.common.model.LoginModel;
import com.hnust.root.common.util.JWTUtil;
import com.hnust.root.dao.entity.domain.OperationLog;
import com.hnust.root.service.SpringContextBeanService;
import com.hnust.root.service.inteface.OperationLogService;
import com.hnust.root.web.annotation.SysLog;
import com.hnust.root.web.config.GlobalConfig;
import org.aspectj.lang.ProceedingJoinPoint;
import org.springframework.scheduling.annotation.Async;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.time.LocalDateTime;
import java.util.Map;

/**
 * @Author zhusir
 * @Date 2019/2/10
 * @Desc 记录日志的实现类
 */
public class LogAspect extends AspectManager {
    private static final Log LOGGER = LogFactory.get();

    private AspectApi aspectApi;

    LogAspect(AspectApi aspectApi) {
        super();
        this.aspectApi = aspectApi;
    }

    @Override
    public Object doHandlerAspect(ProceedingJoinPoint pjp, Method method) throws Throwable {
        aspectApi.doHandlerAspect(pjp, method);
        return doLog(pjp, method);
    }

    /**
     * 具体记录日志的逻辑
     *
     * @param pjp
     * @param method
     * @return
     * @throws Throwable
     */
    @Async
    Object doLog(ProceedingJoinPoint pjp, Method method) throws Throwable {
        SysLog log = method.getAnnotation(SysLog.class);
        String operation = null;
        //异常日志信息
        StackTraceElement[] stack = null;
//        是否发生了异常
        boolean isException = false;
        Object result = null;
        try {
            result = pjp.proceed(pjp.getArgs());
            return result;
        } catch (Throwable throwable) {
            isException = true;
            operation = throwable.getMessage();
            stack = throwable.getStackTrace();
            throw throwable;
        } finally {
            recordLog(pjp, method, log, operation, isException, stack, result);
        }
    }

    /**
     * 记录日志到数据库
     *
     * @param joinPoint
     * @param method
     * @param log
     * @param actionLog
     * @param isException
     * @param stackTrace
     */
    private void recordLog(ProceedingJoinPoint joinPoint,
                           Method method,
                           SysLog log,
                           String actionLog,
                           boolean isException,
                           StackTraceElement[] stackTrace,
                           Object result) {
        ServletRequestAttributes sra = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = sra.getRequest();
        String jwtHeader = request.getHeader(GlobalConfig.getJwtHeader());

        OperationLogService operationLogService = SpringContextBeanService.getBean(OperationLogService.class);

        OperationLog operationLog = new OperationLog();
        if (!StrUtil.isEmpty(jwtHeader)) {
            int userId = JWTUtil.getUserId(jwtHeader);
            operationLog.setUserId(userId);
        }

        operationLog.setCreateTime(LocalDateTime.now());
        operationLog.setIP(HttpUtil.getClientIP(request));
        operationLog.setModelName(log.modelName());
        operationLog.setOperation(log.operation());
        operationLog.setDescription(log.desc());
        operationLog.setClassName(joinPoint.getTarget().getClass().getName());
        operationLog.setMethodName(method.getName());
        operationLog.setSuccess(isException ? 1 : 0);
        operationLog.setResponse(result.toString());

//        发生异常，记录异常信息
        if (isException) {
            StringBuilder sb = new StringBuilder();
            sb.append(actionLog).append(" ");
            for (StackTraceElement aStackTrace : stackTrace) {
                sb.append(aStackTrace).append(" ");
            }
            operationLog.setMessage(sb.toString());
        }
        boolean isJoint = false;
//        记录参数
        Object[] args = joinPoint.getArgs();

        StringBuilder sbArray = new StringBuilder();
        for (Object arg : args) {
            // 如果是登录，则将密码置为*****
//            todo:重置密码时也要更改
            if (arg instanceof LoginModel) {
                if (((LoginModel) arg).getPassword() != null) {
                    ((LoginModel) arg).setPassword("******");
                }
                operationLog.setArgs(arg.toString());
            } else if (arg instanceof String
                    || arg instanceof Long
                    || arg instanceof Integer
                    || arg instanceof Double
                    || arg instanceof Float
                    || arg instanceof Byte
                    || arg instanceof Short
                    || arg instanceof Character) {
                isJoint = true;
            } else if (arg instanceof String[]
                    || arg instanceof Long[]
                    || arg instanceof Integer[]
                    || arg instanceof Double[]
                    || arg instanceof Float[]
                    || arg instanceof Byte[]
                    || arg instanceof Short[]
                    || arg instanceof Character[]) {
                Object[] strs = (Object[]) arg;
                sbArray.append("[");
                for (Object str : strs) {
                    sbArray.append(str.toString()).append(",");
                }
                sbArray.deleteCharAt(sbArray.length() - 1);
                sbArray.append("]");
                operationLog.setArgs(sbArray.toString());
            } else {
                sbArray.append(arg.toString());
                operationLog.setArgs(sbArray.toString());
            }
        }

        if(isJoint){
            Map<String, String[]> parameterMap = request.getParameterMap();
            for (String key:parameterMap.keySet()) {
                String[] strings = parameterMap.get(key);
                for (String str:strings) {
                    sbArray.append(key).append("=").append(str).append(",");
                }
            }
            operationLog.setArgs(sbArray.deleteCharAt(sbArray.length()-1).toString());
        }
        operationLogService.save(operationLog);
    }

}
